from instafeed.subscriptions.models import FeedSubscription
from instafeed.subscriptions.forms import FeedSubscriptionForm
from instafeed.subscriptions.models import CloudServer
from instafeed.subscriptions.forms import CloudServerForm
from instafeed.conduits.models import Conduit
from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
from django.contrib.auth.decorators import login_required
from django.conf import settings
from django.template import RequestContext, loader
from instafeed.tasks import delay_task
from SimpleXMLRPCServer import SimpleXMLRPCDispatcher
dispatcher = SimpleXMLRPCDispatcher()

def notifyUpdate(request, feedId):
    subscription = get_object_or_404(FeedSubscription, pk=feedId)
    # We have to respond quickly to the notifier, so
    # the handling of the event it's done in background
    delay_task("sendNewEntries", feedId=feedId)
    template_params = {}
    if 'challenge' in request.GET:
        template_params['challenge'] = request.GET['challenge']
    if 'challenge' in request.POST:
        template_params['challenge'] = request.POST['challenge']
    if 'challenge' in template_params:
        response = HttpResponse(mimetype='text/plain')
        response.write(template_params['challenge'])
        response['Content-lenght'] = str(len(response.content))
    else:
        response = render_to_response("notifyUpdateResponse.html", template_params,
            context_instance=RequestContext(request))
    return response

@login_required
def subscribe(request, feedId):
    subscription = get_object_or_404(FeedSubscription, pk=feedId)
    if request.user == subscription.user:
        success, msg = subscription.subscribe()
        return HttpResponse("Success: %s, Message: %s" % (success, msg))

@login_required
def dashboard(request):
    feeds = FeedSubscription.objects.filter(user=request.user)
    cloudservers = CloudServer.objects.filter(user=request.user)
    return render_to_response("dashboard.html", {'feeds': feeds, 'cloudservers': cloudservers},
        context_instance=RequestContext(request))

def _readEnabledConduits(request_post):
    enabled_conduits = []
    for conduitId in [int(k[len("conduit_"):]) for k in request_post.keys()
                                               if k.startswith("conduit_")]:
        conduit = Conduit.objects.get(id=conduitId)
        enabled_conduits.append(conduit)
    return enabled_conduits

class ConduitSelectedWrapper:
    """
    Simple wrapper around a conduit to pass selected to the template
    kid, come back home we forgive you...
    """

    def __init__(self, conduit, selected):
        self.conduit = conduit
        self.selected = selected

    def __getattr__(self, attrName):
        return getattr(self.conduit, attrName)

@login_required
def new_feed(request):
    subscriptionError = ""
    if request.method == "POST":
        form = FeedSubscriptionForm(request.POST)
        enabled_conduits = _readEnabledConduits(request.POST)
        if form.is_valid():
            new_feed = FeedSubscription(name=form.cleaned_data["name"],
                        user=request.user, url=form.cleaned_data["url"])
            new_feed.save()
            for c in enabled_conduits:
                new_feed.conduits.add(c)
            new_feed.save() # Save again, with the conduits
            try:
                success, msg = new_feed.subscribe(domain=settings.SITE_NAME)
                if success:
                    return HttpResponseRedirect("/")
                else:
                    subscriptionError = "Error subscribing: '%s'. Please check the URL and try again." % msg
            except Exception, e:
                subscriptionError = "Unknown error subscribing to the feed: %s. Please check the URL is valid and supports rsscloud and try again." % e
            # If I am here, there is some error with the subscription, delete the feed
            new_feed.delete()
    else:
        form = FeedSubscriptionForm()
        enabled_conduits = []
    conduits = Conduit.objects.filter(user=request.user)
    conduits = [ConduitSelectedWrapper(c, c in enabled_conduits) for c in conduits]
    return render_to_response("new_feed.html",
        {'mode': 'new',
         'conduits': conduits,
         'form': form,
         'subscriptionError': subscriptionError},
        context_instance=RequestContext(request))

@login_required
def edit_feed(request, feedId):
    subscription = get_object_or_404(FeedSubscription, pk=feedId)
    subscriptionError = ""
    if request.method == "POST":
        form = FeedSubscriptionForm(request.POST)
        enabled_conduits = _readEnabledConduits(request.POST)
        if form.is_valid():
            # Save first the url and test subscription
            # if fails restore the old url, return to the form
            old_url = subscription.url
            subscription.url = form.cleaned_data["url"]
            subscription.save()
            success = False
            try:
                success, msg = subscription.subscribe(domain=settings.SITE_NAME)
                if not success:
                    subscriptionError = "Error subscribing: '%s'. Please check the URL and try again." % msg
            except Exception, e:
                subscriptionError = "Unknown error subscribing to the feed: %s. Please check the URL is valid and supports rsscloud and try again." % e
            if success:
                subscription.name = form.cleaned_data["name"]
                # Clean and readd selected conduits
                subscription.conduits.clear()
                for c in enabled_conduits:
                    subscription.conduits.add(c)
                subscription.save()
                return HttpResponseRedirect("/")
    else:
        form = FeedSubscriptionForm(initial={'name': subscription.name,
            'url': subscription.url})
        enabled_conduits = subscription.conduits.all()
    conduits = Conduit.objects.filter(user=request.user)
    conduits = [ConduitSelectedWrapper(c, c in enabled_conduits) for c in conduits]
    return render_to_response("new_feed.html",
        {'mode': 'edit',
         'conduits': conduits,
         'form': form,
         'subscriptionError': subscriptionError},
        context_instance=RequestContext(request))

@login_required
def delete_feed(request, feedId):
    subscription = get_object_or_404(FeedSubscription, pk=feedId)
    if subscription.user == request.user:
        subscription.delete()
    return HttpResponseRedirect("/")

def xmlrpc_feed(request, feedId):
    "allows the feed to receive notifications via xml-rpc"
    feed = get_object_or_404(FeedSubscription, pk=feedId)
    def ping(*args):
        delay_task("sendNewEntries", feedId=feedId)
        resp = True
        if len(args) == 1:
            resp = args[0]
        return resp
    dispatcher.register_function(ping, 'rssCloud.ping')
    response = HttpResponse(mimetype='application/xml')
    response.write(dispatcher._marshaled_dispatch(request.raw_post_data))
    response['Content-lenght'] = str(len(response.content))
    return response

# Cloud Servers
# =============

@login_required
def new_cloudserver(request):
    template_params = {}
    template_params["next"] = request.GET.get("next", request.POST.get("next", "/"))

    if request.method == "POST":
        form = CloudServerForm(request.POST)
        if form.is_valid():
            new_cloudserver = form.save(commit=False)
            new_cloudserver.user = request.user
            new_cloudserver.save()
            return HttpResponseRedirect(template_params["next"])
        template_params['form'] = form
    elif request.method == "GET":
        template_params['form'] = CloudServerForm()
    return render_to_response("new_cloudserver.html", template_params,
        context_instance=RequestContext(request))

@login_required
def edit_cloudserver(request, cloudserverId):
    cloudserver = get_object_or_404(CloudServer, pk=cloudserverId)
    subscriptionError = ""
    if request.method == "POST":
        form = CloudServerForm(request.POST)
        if form.is_valid():
            old_url = cloudserver.url
            cloudserver.url = form.cleaned_data["url"]
            cloudserver.save()
            cloudserver.name = form.cleaned_data["name"]
            cloudserver.save()
            return HttpResponseRedirect("/")
    elif request.method == "GET":
        form = CloudServerForm(initial={'name': cloudserver.name, 'url': cloudserver.url})
    return render_to_response("new_cloudserver.html", 
        {'mode': 'edit',
         'form': form,
         'subscriptionError': subscriptionError},
        context_instance=RequestContext(request))

@login_required
def delete_cloudserver(request, cloudserverId):
    cloudserver = get_object_or_404(CloudServer, pk=cloudserverId)
    if cloudserver.user == request.user:
        cloudserver.delete()
    return HttpResponseRedirect("/")

def ping_cloudserver(request, cloudserverId):
    """receives notification updates for the cloud server
       on update regenerates the cloudfeed for this server
    """
    result = 'err'
    resp_type = 'html' if request.user.is_authenticated() else 'txt'
    cloudserver = get_object_or_404(CloudServer, pk=cloudserverId)
    if 'url' in request.POST and request.POST['url'] == cloudserver.url:
        delay_task("notifySubscribers", cloudserverId=cloudserver.id)
        cloudserver.gen_cloudfeed()
        result = 'ack'
    template = '%s_up_cloudserver.%s' % (result, resp_type)
    return render_to_response(template, {'cloudserver': cloudserver},
        context_instance=RequestContext(request))

def help_cloudserver(request, cloudserverId):
    "shows instructions to user a cloudserver"
    cloudserver = get_object_or_404(CloudServer, pk=cloudserverId)
    return render_to_response("help_cloudserver.html", {'cloudserver': cloudserver,
        'domain': settings.SITE_NAME}, context_instance=RequestContext(request))

def subscribe_to_cloudserver(request, cloudserverId):
    "add user to the list of cloud server subscribers"
    cloudserver = get_object_or_404(CloudServer, pk=cloudserverId)
    req_params = ('notifyProcedure', 'port', 'path', 'protocol')
    try:
        params = dict([(x, request.POST[x]) for x in req_params])
    except KeyError:
        return HttpResponseBadRequest('Missing required parameter for subscription')
    params['urls'] = [request.POST[x] for x in request.POST.keys()
                                       if x[:3] == 'url' and x[3:].isdigit()]
    params['ip'] = request.META['REMOTE_ADDR']
    if 'domain' in request.POST:
        params['domain'] = request.POST['domain']
    cloudserver.subscribe(**params)
    xml = loader.render_to_string('subscribed_ok.xml', {'cloudserver': cloudserver})
    response = HttpResponse(xml, mimetype='text/xml')
    return response

def feed_cloudserver(request, cloudserverId):
    "adds a rsscloud tag to any rss feed"
    cloudserver = get_object_or_404(CloudServer, pk=cloudserverId)
    try:
        cloudfeed = open(cloudserver.get_cloudfeed_path())
    except IOError:
        cloudserver.gen_cloudfeed()
        cloudfeed = open(cloudserver.get_cloudfeed_path())
    xml = cloudfeed.read()
    cloudfeed.close()
    return HttpResponse(xml, mimetype=' text/xml')

def web_cloudserver(request, cloudserverId):
    "shows a web page with a link to the rss feed, used for some subscribers"
    cloudserver = get_object_or_404(CloudServer, pk=cloudserverId)
    return render_to_response('web_cloudserver.html', {'cloudserver': cloudserver,
        'domain': settings.SITE_NAME}, context_instance=RequestContext(request))

def xmlrpc_cloudserver(request, cloudserverId):
    "allows the cloud server to receive subscriptions via xml-rpc"
    cloudserver = get_object_or_404(CloudServer, pk=cloudserverId)
    def pleaseNotify(*args, **kwargs):
        kwargs['ip'] = request.META['REMOTE_ADDR']
        return cloudserver.subscribe(*args, **kwargs)
    dispatcher.register_function(pleaseNotify, 'pleaseNotify')
    response = HttpResponse(mimetype='application/xml')
    response.write(dispatcher._marshaled_dispatch(request.raw_post_data))
    response['Content-lenght'] = str(len(response.content))
    return response
